package com.yvan;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.collect.Lists;
import com.yvan.platform.JsonWapper;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

public class YvanUtil {

    public static final String VIEW_HISTORY_COOKIE_KEY = "client_view_history";
    public static final int COOKIE_AGE = 30 * 24 * 60 * 60;
    public static final String COOKIE_PATH = "/";
    private static final sun.misc.BASE64Encoder base64Encoder = new sun.misc.BASE64Encoder();
    private static final sun.misc.BASE64Decoder base64Decoder = new sun.misc.BASE64Decoder();
    private static final char[] DIG_ARRAY = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
    private static ObjectMapper objectMapper;
    private static ObjectMapper objectYamlMapper;
    private static ResourceLoader loader = new DefaultResourceLoader();

    static {
        objectMapper = new ObjectMapper();
        objectMapper.writerWithDefaultPrettyPrinter();

        objectYamlMapper = new ObjectMapper(new YAMLFactory());
        objectYamlMapper.writerWithDefaultPrettyPrinter();
    }

    public static char getShortDig(int num) {
        return DIG_ARRAY[num];
    }


    /**
     * crm.yyjzt.com[:80] -> yyjzt.com
     * aaaa.test.yyjzt.com[:80] -> yyjzt.com
     * 192.168.1.1[:80] -> 192.168.1.1
     * localhost[:80] -> localhost
     *
     * @return yyjzt.com / 192.168.1.1 / localhost
     */
    public static String rootDomain(String hostName) {
        hostName = hostName.toLowerCase();
        int portPos = hostName.indexOf(':');
        if (portPos > 0) {
            hostName = hostName.substring(0, portPos);
        }

        if (isDomain(hostName)) {
            int pos = hostName.lastIndexOf('.');
            if (pos < 0) {
                return hostName;
            }

            int pos2 = hostName.lastIndexOf('.', pos - 1);
            if (pos2 < 0) {
                return hostName;
            }

            return hostName.substring(pos2 + 1);
        }
        return hostName;
    }

    /**
     * crm.yyjzt.com[:80] -> true
     * aaaa.test.yyjzt.com[:80] -> true
     * 192.168.1.1[:80] -> false
     * localhost[:80] -> false
     *
     * @return 判断hostName是不是域名
     */
    public static boolean isDomain(String hostName) {
        hostName = hostName.toLowerCase();
        int portPos = hostName.indexOf(':');
        if (portPos > 0) {
            hostName = hostName.substring(0, portPos);
        }

        return (hostName.endsWith(".com") ||
                hostName.endsWith(".net") ||
                hostName.endsWith(".org") ||
                hostName.endsWith(".cn") ||
                hostName.endsWith(".gov") ||
                hostName.endsWith(".edu") ||
                hostName.endsWith(".tt") ||
                hostName.endsWith(".me"));
    }

    public static JsonWapper loadYamlResource(String resLocation) {
        Resource resource = loader.getResource(resLocation);

        JsonNode jsonNode = null;
        InputStream is = null;
        try {
            is = resource.getInputStream();
            jsonNode = objectYamlMapper.readTree(is);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(is);
        }

        try {
            return new JsonWapper(objectMapper.writeValueAsString(jsonNode));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static JsonWapper yamlToJsonWapper(String yamlContent) {
        try {
            JsonNode jsonNode = objectYamlMapper.readTree(yamlContent);
            return new JsonWapper(objectMapper.writeValueAsString(jsonNode));

        } catch (Exception e) {
            throw new RuntimeException(e);

        }
    }

    public static JsonWapper readYamlWapper(Collection<String> locations) {
        JsonNode jsonNode = null;
        for (String location : locations) {
            Resource resource = loader.getResource(location);

            InputStream is = null;
            try {
                is = resource.getInputStream();
                if (jsonNode == null) {
                    jsonNode = objectYamlMapper.readTree(is);
                } else {
                    merge(jsonNode, objectYamlMapper.readTree(is));
                }
            } catch (Exception e) {
                String errorInfoAppend = ("error read file location:" + location);
                System.out.print(errorInfoAppend);
                throw new RuntimeException(e);
            } finally {
                IOUtils.closeQuietly(is);
            }
        }

        if (jsonNode == null) {
            return new JsonWapper();
        }

        try {
            return new JsonWapper(objectMapper.writeValueAsString(jsonNode));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static JsonWapper merge(String mainNode, String updateNode) {
        try {
            return new JsonWapper(objectMapper.writeValueAsString(merge(objectMapper.readTree(mainNode), objectMapper.readTree(updateNode))));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static JsonNode readYamlNode(Collection<String> locations) {
        JsonNode jsonNode = null;
        for (String location : locations) {
            Resource resource = loader.getResource(location);

            InputStream is = null;
            try {
                is = resource.getInputStream();
                if (jsonNode == null) {
                    jsonNode = objectYamlMapper.readTree(is);
                } else {
                    merge(jsonNode, objectYamlMapper.readTree(is));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                IOUtils.closeQuietly(is);
            }
        }

        return jsonNode;
    }

    public static JsonWapper node2Wapper(JsonNode jsonNode) {
        if (jsonNode == null) {
            return new JsonWapper();
        }

        try {
            return new JsonWapper(objectMapper.writeValueAsString(jsonNode));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static JsonNode readYamlInner(SettingProperties settingProperties) {
        if (settingProperties == null || settingProperties.getImports() == null) {
            return null;
        }
        if (settingProperties.getImports().size() <= 0) {
            return NullNode.instance;
        }

        JsonNode jsonNode = null;
        for (String f : settingProperties.getImports()) {
            final String location = settingProperties.getUrl() + (f.startsWith("/") ? f : "/" + f);
            Resource resource = loader.getResource(location);

            InputStream is = null;
            try {
                is = resource.getInputStream();
                JsonNode jsonNodeNew = objectYamlMapper.readTree(is);

                if (jsonNodeNew.isObject() && jsonNodeNew.has("imports")) {
                    JsonNode importsNode = jsonNodeNew.get("imports");

                    List<String> imports = null;
                    if (importsNode.isArray()) {
                        ArrayNode arrayNode = (ArrayNode) importsNode;
                        imports = Lists.newArrayList();
                        for (int i = 0; i < arrayNode.size(); i++) {
                            imports.add(arrayNode.get(i).asText());
                        }

                    } else if (importsNode.isTextual()) {
                        imports = Lists.newArrayList(importsNode.asText());
                    }

                    JsonNode jsonNodeImport = null;
                    if (imports != null && imports.size() > 0) {
                        SettingProperties n = new SettingProperties();
                        n.setUrl(settingProperties.getUrl());
                        n.setImports(imports);
                        jsonNodeImport = readYamlInner(n);
                    }

                    if (jsonNodeImport != null) {
                        merge(jsonNodeNew, jsonNodeImport);
                    }
                    ((ObjectNode) jsonNodeNew).remove("imports");
                }

                if (jsonNode == null) {
                    jsonNode = jsonNodeNew;
                } else {
                    merge(jsonNode, jsonNodeNew);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                IOUtils.closeQuietly(is);
            }
        }
        return jsonNode;
    }


    public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {

        Iterator<String> fieldNames = updateNode.fieldNames();
        while (fieldNames.hasNext()) {

            String fieldName = fieldNames.next();
            JsonNode mainJsonNode = mainNode.get(fieldName);
            JsonNode updateJsonNode = updateNode.get(fieldName);

            // if field exists and is an embedded object
            if (mainJsonNode != null && mainJsonNode.isObject()) {
                merge(mainJsonNode, updateNode.get(fieldName));
            } else {
                if (mainJsonNode != null && mainJsonNode.isArray()) {
                    if (updateJsonNode != null) {
                        ArrayNode arrayNode = (ArrayNode) mainJsonNode;
                        if (updateJsonNode.isArray()) {
                            //合并2个数组
                            arrayNode.addAll((ArrayNode) updateJsonNode);
                        } else {
                            arrayNode.add(updateJsonNode);
                        }
                    }
                } else if (mainNode instanceof ObjectNode) {
                    // Overwrite field
                    JsonNode value = updateNode.get(fieldName);
                    ((ObjectNode) mainNode).set(fieldName, value);
                }
            }

        }

        return mainNode;
    }

    public static JsonWapper readJsonWapper(String location) {
        return readJsonWapper(loader.getResource(location));
    }

    public static JsonWapper readJsonWapper(Resource resource) {
        if (resource != null && resource.exists()) {
            if (resource.isReadable()) {
                InputStream is = null;
                try {
                    is = resource.getInputStream();
                    return new JsonWapper(is);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    IOUtils.closeQuietly(is);
                }
            }
        }
        throw new RuntimeException("resource can't found!");
    }

    public static String joinCollection(Iterable<?> iterable, String spt, String emptyString) {
        StringBuilder sb = new StringBuilder();
        String sp = "";
        Iterator<?> iter = iterable.iterator();

        while (iter.hasNext()) {
            Object o = iter.next();
            sb.append(sp).append(Conv.NS(o));
            sp = spt;
        }
        if (sb.length() <= 0) {
            return emptyString;
        } else {
            return sb.toString();
        }
    }

    public static String joinCollection(Iterable<?> iterable, String spt) {
        return joinCollection(iterable, spt, "");
    }

    public static <T> T mapToEntities(Object listMap, Class<T> clazz) {
        try {
            String jsonString = objectMapper.writeValueAsString(listMap);
            return objectMapper.readValue(jsonString, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将Json字符串序列化成指定对象
     */
    public static <T> T jsonToObj(String jsonStr, Class<T> clazz) {
        try {
            return objectMapper.readValue(jsonStr, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 保存内容到文件
     *
     * @param filePath 文件路径
     * @param content  保存的内容
     */
    public static void saveToFile(String filePath, String content) {
        BufferedWriter bufferedWriter = null;
        OutputStreamWriter outputStreamWriter = null;
        FileOutputStream fileOutputStream = null;
        try {
            File file = new File(filePath);
            fileOutputStream = new FileOutputStream(file);
            outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
            bufferedWriter = new BufferedWriter(outputStreamWriter);
            bufferedWriter.write(content);
            bufferedWriter.flush();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(bufferedWriter);
            IOUtils.closeQuietly(outputStreamWriter);
            IOUtils.closeQuietly(fileOutputStream);
        }
    }

    /**
     * 将Json字符串序列化成 list
     */
    public static List<?> jsonToList(String jsonInput) {
        try {
            return objectMapper.readValue(jsonInput, List.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将Json字符串序列化成json
     */
    public static Map<?, ?> jsonToMap(String jsonInput) {
        try {
            return objectMapper.readValue(jsonInput, Map.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将对象序列化成json
     */
    public static String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将对象序列化成json
     */
    public static String toJsonPretty(Object obj) {
        try {
            return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从文件中读取所有内容
     */
    public static String readFile(File file) throws IOException {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            return StringUtils.join(IOUtils.readLines(fis, "UTF-8"), "\r\n");
        } finally {
            IOUtils.closeQuietly(fis);
        }
    }

    public static <T> T createInstance(Class<T> clazz, String classFullName,
                                       Object... args) throws ClassNotFoundException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        Constructor constructor = Class.forName(classFullName).getConstructors()[0];
        return (T) constructor.newInstance(args);
    }

    public static String encodeBase64(String s) {
        return base64Encoder.encode(s.getBytes());
    }

    // 将 BASE64 编码的字符串 s 进行解码
    public static String decodeBase64(String s) {
        try {
            byte[] b = base64Decoder.decodeBuffer(s);
            return new String(b, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 得到当前request请求的所有cookie
     *
     * @return cookie数组
     * @author jianguo.xu
     */
    public static Cookie[] getCookies() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        return request == null ? null : request.getCookies();
    }

    /**
     * 根据cookie名字取得cookie
     *
     * @param name cookie名字
     * @return cookie
     * @author jianguo.xu
     */
    public static Cookie getCookie(String name) {
        Cookie[] cookies = getCookies();
        if (cookies != null && cookies.length > 0) {
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                String cookName = cookie.getName();
                if (cookName != null && cookName.equals(name)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    public static String getCookieValue(String name) {
        Cookie cookie = getCookie(name);
        if (cookie == null) {
            return null;
        }
        return cookie.getValue();
    }

    /**
     * 将cookie写入客户端
     *
     * @param cookie
     * @author jianguo.xu
     */
    public static void writeCookie(Cookie cookie) {
        if (cookie == null) return;
        ServletRequestAttributes attributes = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes());
        HttpServletResponse response = attributes.getResponse();
        HttpServletRequest request = attributes.getRequest();
        /*
         * if (request != null) { String host = request.getHeader("Host"); if (ConfigUtils.WEBSITE.equals(host))
         * cookie.setDomain("." + ConfigUtils.DOMAIN); }
         */
        if (response != null) {
            response.addCookie(cookie);
        }
    }

    public static void removeCookie(String cookieName, String path) {
        ServletRequestAttributes attributes = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes());
        HttpServletResponse response = attributes.getResponse();
        HttpServletRequest request = attributes.getRequest();

        Cookie[] cookies = request.getCookies();
        if (cookies == null || cookies.length == 0) return;

        for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];
            if (cookie.getName().equals(cookieName)) {
                cookie.setMaxAge(0);
                cookie.setPath(path);
                /*
                 * String host = request.getHeader("Host"); if (ConfigUtils.WEBSITE.equals(host)) cookie.setDomain("." +
                 * ConfigUtils.DOMAIN);
                 */
                response.addCookie(cookie);
                break;
            }
        }

    }

    public static String urlEncoding(String value) {
        try {
            byte[] bs = Base64.encodeBase64URLSafe(value.getBytes("UTF-8"));
            return new String(bs, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("encode error.", e);
        }
    }

    @SuppressWarnings("unchecked")
    public static <K, V> Map<K, V> bean2Map(Object javaBean) {
        Map<K, V> ret = new HashMap<K, V>();
        try {
            Method[] methods = javaBean.getClass().getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().startsWith("get")) {
                    String field = method.getName();
                    field = field.substring(field.indexOf("get") + 3);
                    field = field.toLowerCase().charAt(0) + field.substring(1);
                    Object value = method.invoke(javaBean, (Object[]) null);
                    ret.put((K) field, (V) (null == value ? "" : value));
                }
            }
        } catch (Exception e) {
        }
        return ret;
    }

    private static String digits(long val, int digits) {
        long hi = 1L << (digits * 4);
        return Numbers.toString(hi | (val & (hi - 1)), Numbers.MAX_RADIX)
                .substring(1);
    }

    public static String createUUID() {
        UUID uuid = UUID.randomUUID();
        StringBuilder sb = new StringBuilder();
        sb.append(digits(uuid.getMostSignificantBits() >> 32, 8));
        sb.append(digits(uuid.getMostSignificantBits() >> 16, 4));
        sb.append(digits(uuid.getMostSignificantBits(), 4));
        sb.append(digits(uuid.getLeastSignificantBits() >> 48, 4));
        sb.append(digits(uuid.getLeastSignificantBits(), 12));
        return sb.toString();
    }

    public static InetAddress getInetAddress() {
        try {
            return InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            return null;
        }
    }

    public static String getHostIp() {
        return getHostIp(getInetAddress());
    }

    public static String getHostName() {
        return getHostName(getInetAddress());
    }

    public static String getHostIp(InetAddress netAddress) {
        if (null == netAddress) {
            return null;
        }
        return netAddress.getHostAddress();
    }

    public static String getHostName(InetAddress netAddress) {
        if (null == netAddress) {
            return null;
        }
        return netAddress.getHostName();
    }


    /**
     * 判断是否是 Ajax 请求
     *
     * @param request
     * @return
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String requestedWithHeader = request.getHeader("X-Requested-With");
        return "XMLHttpRequest".equals(requestedWithHeader);
    }
}
